﻿using System;
using SoftwareConsulting.BI11.NumericalMethods.Iterations;

namespace SoftwareConsulting.BI11.NumericalMethods.MatrixAlgebra
{
    /// <summary>
    /// Класс нахождения наибольшего собственного значения и соответствующего вектора матрицы
    /// </summary>
    public class LargestEigenValueFinder : IterativeProcess
    {
        private double _eigenValue;
        private Vector _eigenVector;
        private Vector _transposedEigenVector;
        private Matrix _matrix;

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="precision"></param>
        /// <param name="matrix"></param>
        public LargestEigenValueFinder(double precision, Matrix matrix) : 
            this(matrix)
        {
            SetDesiredPrecision(precision);
        }

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="matrix"></param>
        public LargestEigenValueFinder(Matrix matrix)
        {
            _matrix = matrix;
            _eigenValue = double.NaN;
        }

        /// <summary>
        /// Возвращает собственное значение матрицы
        /// </summary>
        public double GetEigenValue()
        {
            return _eigenValue;
        }

        /// <summary>
        /// Вощвращает нормализованный собственный вектор
        /// </summary>
        public Vector GetNormalizedEigenVector()
        {
            return _eigenVector * (1.0 / _eigenVector.GetNorm());
        }

        /// <summary>
        /// Выполняет итерацию
        /// </summary>
        /// <returns></returns>
        public override double DoIteration()
        {
            double oldEigenValue = _eigenValue;
            _transposedEigenVector = _transposedEigenVector.GetResultOfSelfTransposedMultiplication(_matrix);
            _transposedEigenVector *= (1.0 / _transposedEigenVector[0]);
            _eigenVector = _matrix.GetResultOfMultiplication(_eigenVector);
            _eigenValue = _eigenVector[0];
            _eigenVector *= (1.0 / _eigenValue);
            return double.IsNaN(oldEigenValue)
                            ? 10 * this.DesiredPrecision
                            : Math.Abs(_eigenValue - oldEigenValue);
        }

        /// <summary>
        /// Инициализирует итерации
        /// </summary>
        public override void CreateRandomPopulation()
        {
            _eigenValue = double.NaN;
            int n = _matrix.ColumnsCount;
            double[] eigenvectorComponents = new double[n];
            for (int i = 0; i < n; i++)
                eigenvectorComponents[i] = 1.0;
            _eigenVector = new Vector(eigenvectorComponents);
            n = _matrix.RowsCount;
            eigenvectorComponents = new double[n];
            for (int i = 0; i < n; i++)
                eigenvectorComponents[i] = 1.0;
            _transposedEigenVector = new Vector(eigenvectorComponents);
        }

        /// <summary>
        /// Возвращает искатель следующего самого большого собственного значения матрицы
        /// </summary>
        /// <returns></returns>
        public LargestEigenValueFinder GetNextLargestEigenValueFinder()
        {
            double norm = 1.0 / _eigenVector.GetDotProduct(_transposedEigenVector);
            Vector v1 = _eigenVector * norm;
            return new LargestEigenValueFinder(this.DesiredPrecision,
                    _matrix.GetResultOfMultiplication(SymmetricMatrix.IdentityMatrix(v1.Dimensionality)
                                .GetResultOfSubtraction(v1.GetTensorProduct(_transposedEigenVector))));
        }

        /// <summary>
        /// Возвращает строковое представление найденного самого большого собственного значения и соответствующего вектора матрицы
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Format("{0} ({1})", _eigenValue, _eigenVector);
        }
    }
}
